package org.jeecg.common.util.blockchain;


import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.util.PmsUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ExecutionException;

@Slf4j
@Component
public class RpcGethUtils {


    //  public static final String HTTPS_MAINNET_URL = "https://mainnet.infura.io/v3/720246bcdf6046708fb18848c6b2bb63";
    //public static final String HTTPS_MAINNET_URL = "https://ropsten.infura.io/v3/720246bcdf6046708fb18848c6b2bb63";

    private static String net_url;

    @Value("${wallet.geth.net_url:https://mainnet.infura.io/v3/720246bcdf6046708fb18848c6b2bb63}")
    public void setNet_url(String net_url) {
        RpcGethUtils.net_url = net_url;
    }


    private static String storageFile_path;

    @Value("${wallet.geth.storageFile_path:d://}")
    public void setStorageFile_path(String storageFile_path) {
        RpcGethUtils.storageFile_path = storageFile_path;
    }


    /**
     * 创建地址
     */
    public static WalletResult createAddress(String password) {
        WalletResult result = new WalletResult();
        try {
            Web3j web3j = Web3j.build(new HttpService(net_url));
            File file = new File(storageFile_path + "/ethFiles");
            if (!file.exists()) {
                file.mkdir();
            }
            String address = WalletUtils.generateLightNewWalletFile(password, file);
            address = "0x" + address.substring(address.lastIndexOf("--") + 2, address.lastIndexOf("."));
            result.setSuccess(false);
            if (StringUtils.isNotEmpty(address)) {
                JSONObject data = new JSONObject();
                data.put("address", address);
                result.setData(data);
                result.setSuccess(true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @return
     * @Desc 根据hash查询交易记录
     */
    public WalletResult getTransactionByHash(String hash) {
        WalletResult result = new WalletResult();
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            org.web3j.protocol.core.methods.response.Transaction transaction = web3j.ethGetTransactionByHash(hash).send().getTransaction().get();
            if (transaction != null) {
                result.setData(transaction);
                result.setSuccess(true);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Desc 根据transactionHash获取交易收据
     */
    public TransactionReceipt getTransactionReceipt(String transactionHash) {
        TransactionReceipt receipt = new TransactionReceipt();
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            receipt = web3j.ethGetTransactionReceipt(transactionHash).send().getTransactionReceipt().get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return receipt;
    }


    /**
     * @Desc 根据地址查询余额
     */
    public BigInteger getBalance(String address) {
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            if (null != web3j) {
                EthGetBalance ethBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
                BigInteger balance = ethBalance.getBalance();

                if (balance != null) {
                    return balance;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @Desc 根据区块高度查询交易记录
     */
    public WalletResult getBlockByNumbe(BigInteger number, boolean isdetail) {
        WalletResult result = new WalletResult();
        DefaultBlockParameter blockNumber = DefaultBlockParameter.valueOf(number);
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            EthBlock.Block block = web3j.ethGetBlockByNumber(blockNumber, isdetail).send().getBlock();
            if (block != null) {
                @SuppressWarnings("rawtypes")
                List<EthBlock.TransactionResult> transactions = block.getTransactions();
                if (transactions != null && transactions.size() > 0) {
                    result.setData(transactions);
                    result.setSuccess(true);
                }
            }
        } catch (Exception e) {
            result.setSuccess(false);
            result.setMsg(e.getMessage());
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Desc 获取区块高度
     * @author: SHANGXL
     * @param: @return
     * @return: JsonResult
     * @Date :  	2018年5月14日 下午6:01:31
     */
    public WalletResult getBlockHigh() {
        WalletResult result = new WalletResult();
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            BigInteger blockNumber = web3j.ethBlockNumber().send().getBlockNumber();
            result.setData(blockNumber);
            result.setSuccess(true);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setMsg(e.getMessage());
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Desc 获取coinBase地址
     */
    public WalletResult getCoinBase() {
        WalletResult result = new WalletResult();
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            String coinbase = web3j.ethCoinbase().send().getAddress();
            if (StringUtils.isNotEmpty(coinbase)) {
                result.setMsg(coinbase);
                result.setSuccess(true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Desc 查询代币的余额
     */
    public WalletResult getTokenBalance(String address, String contractAddress) {
        WalletResult result = new WalletResult();
        Web3j web3j = Web3j.build(new HttpService(net_url));
        Function fn = new Function("balanceOf", Arrays.asList(new Address(address)), Collections.<TypeReference<?>>emptyList());
        String data = FunctionEncoder.encode(fn);
        Transaction transaction = new Transaction(null, null, null, null, contractAddress, null, data);
        try {
            String balanceWeiStr = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send().getValue();
            if (StringUtils.isNotEmpty(balanceWeiStr)) {
                BigInteger balanceWei = Numeric.decodeQuantity(balanceWeiStr);
                result.setData(balanceWei);
                result.setSuccess(true);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Desc 以太坊代币转币
     */
    public WalletResult sendTransactionToken(String from, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, BigInteger value, String password, String contractAddress) {
        WalletResult result = new WalletResult();
        Web3j web3j = Web3j.build(new HttpService(net_url));
        try {
            File walletDir = new File(storageFile_path + "/ethFiles/" + from + ".json");
            Credentials credentials = WalletUtils.loadCredentials(password, walletDir);
            String fromAddress = credentials.getAddress();
            EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                    fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
            Address address = new Address(to);
            List<Type> parametersList = new ArrayList<>();
            parametersList.add(address);

            Uint256 value256 = new Uint256(value);
            parametersList.add(value256);
            List<TypeReference<?>> outList = new ArrayList<>();
            Function function = new Function("transfer", parametersList, outList);
            String encodedFunction = FunctionEncoder.encode(function);
            RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice,
                    gasLimit, contractAddress, encodedFunction);
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);

            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
            String hash = ethSendTransaction.getTransactionHash();
            if (StringUtils.isNotEmpty(hash)) {
                result.setSuccess(true);
                result.setData(hash);
            } else {
                result.setSuccess(false);
                result.setData("");
                result.setMsg("提币失败，返回hash为空");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }


    /**
     * 以太坊代币转账接口
     */
    public WalletResult sendTransactionofContract(String from, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, BigInteger value, String password, String contractAddress) {
        WalletResult result = new WalletResult();
        Web3j web3j = Web3j.build(new HttpService(
                net_url));
        try {
            String jsonfile = getEthFileName(from.substring(2));
            if (StringUtils.isNotEmpty(jsonfile)) {
                File walletDir = new File(storageFile_path + "/ethFiles/" + jsonfile);
                Credentials credentials = WalletUtils.loadCredentials(password, walletDir);
                EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                        from, DefaultBlockParameterName.LATEST).sendAsync().get();
                Address address = new Address(to);
                List<Type> parametersList = new ArrayList<>();
                parametersList.add(address);

                Uint256 value256 = new Uint256(value);
                parametersList.add(value256);
                List<TypeReference<?>> outList = new ArrayList<>();

                Function function = new Function("transfer", parametersList, outList);
                String encodedFunction = FunctionEncoder.encode(function);
                RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice,
                        gasLimit, contractAddress, encodedFunction);
                byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);

                String hexValue = Numeric.toHexString(signedMessage);

                EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
                String hash = ethSendTransaction.getTransactionHash();
                if (StringUtils.isNotEmpty(hash)) {
                    result.setSuccess(true);
                    result.setData(hash);
                } else {
                    result.setSuccess(false);
                    result.setData("");
                    result.setMsg("提币失败，返回hash为空");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CipherException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        return result;
    }


    /*    */

    /**
     * erc20代币转账
     *
     * @param from     转账地址
     * @param to       收款地址
     * @param value    转账金额
     * @param password 转账这私钥
     */
    public WalletResult send(String from, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, BigInteger value, String data, String password) {
        WalletResult result = new WalletResult();

        Web3j web3j = Web3j.build(new HttpService(net_url));
        String jsonfile = getEthFileName(from.substring(2));
        if (StringUtils.isNotEmpty(jsonfile)) {
            File walletDir = new File(storageFile_path + "/ethFiles/" + jsonfile);
            Credentials credentials = null;
            try {
                credentials = WalletUtils.loadCredentials(password, walletDir);
                TransactionReceipt receipt = Transfer.sendFunds(web3j, credentials, to, new BigDecimal(value), Convert.Unit.WEI).send();
                String transaction_result = receipt.getTransactionHash();

                if (StringUtils.isNotEmpty(transaction_result)) {
                    result.setData(transaction_result);
                    result.setSuccess(true);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public static String getEthFileName(String eth_Address) {

        String eth_filename = "";
        String[] list = new File(storageFile_path + "/ethFiles").list();
        for (String filename : list) {

            if (filename.indexOf(eth_Address) != -1) {
                eth_filename = filename;
                break;
            }
        }

        return eth_filename;
    }

    public WalletResult allAccounts() {
        WalletResult walletResult = new WalletResult();
        String[] list = new File(storageFile_path + "/ethFiles").list();
        List<String> accounts = new ArrayList<>();
        for (String filename : list) {
            String address = "0x" + filename.substring(filename.lastIndexOf("--") + 2, filename.lastIndexOf("."));
            accounts.add(address);
        }
        walletResult.setSuccess(true);
        walletResult.setData(accounts);
        return walletResult;
    }


    public static void testContract(String contractAddress) {

        //私钥
        String privateKey = "";
        //币合约地址
        // String contractAddress = "0xa4dF8a11d5D66640E6084b1309fBE1cC5a84f52c";
        //转账人私钥
        System.out.println("-------------1111---------->>>>");
        Credentials credentials = Credentials.create(privateKey);
        try {
            Web3j web3j = Web3j.build(new HttpService(net_url));
            EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
            System.out.println("----------------------->>>>"+ethGasPrice.getGasPrice());
            BigInteger gasPrice = ethGasPrice.getGasPrice().multiply(new BigInteger("105")).divide(new BigInteger("100"));
            //gasLimit
            BigInteger gasLimit = BigInteger.valueOf(100000);
            //调用合约
          //  Erc20Sol contract = Erc20Sol.load(contractAddress, web3j, credentials, gasPrice, gasLimit);
            //授信合约地址，授信金额
           // TransactionReceipt transactionReceipt = contract.approve("", new BigInteger("")).send();
            //System.out.println("Hash：" + transactionReceipt.getTransactionHash());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
//        WalletResult walletResult = new RpcGethUtils().createAddress("");
//        if (walletResult.getSuccess()) {
//            //System.out.println(walletResult.getData());
//        }

        testContract("0xa4dF8a11d5D66640E6084b1309fBE1cC5a84f52c");
    }

}